package com.kerbores.agent.server;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Writer;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Modifier;
import java.net.Socket;
import java.util.List;

import org.nutz.http.Request;
import org.nutz.http.Request.METHOD;
import org.nutz.http.sender.PostSender;
import org.nutz.json.Json;
import org.nutz.json.JsonFormat;
import org.nutz.lang.Lang;
import org.nutz.lang.Strings;
import org.nutz.lang.Tasks;
import org.nutz.lang.util.NutMap;
import org.nutz.log.Log;
import org.nutz.log.Logs;
import org.nutz.net.SocketHandler;
import org.nutz.net.TcpServer;
import org.nutz.resource.Scans;

import com.kerbores.agent.InvokeData;
import com.kerbores.agent.Method;
import com.kerbores.agent.MethodName;
import com.kerbores.agent.NameSpace;
import com.kerbores.agent.Result;

public class AgentServer {

	Log log = Logs.get();
	/*
	 * 监听的服务包名
	 */
	private String packageName;
	/**
	 * 状态报告地址
	 */
	private String reportUrl;
	/**
	 * tcp监听端口
	 */
	private int port = 42425;
	/**
	 * 汇报时间,单位秒
	 */
	private long reportTime = 5;

	private StatusGather gather;

	private List<Class<?>> serviceClasses;

	/**
	 * 
	 * @param packageName
	 * @param reportUrl
	 * @param port
	 * @param reportTime
	 * @param gather
	 */
	public AgentServer(String packageName, String reportUrl, int port, long reportTime, StatusGather gather) {
		super();
		this.packageName = packageName;
		this.reportUrl = reportUrl;
		this.port = port;
		this.gather = gather;
		this.reportTime = reportTime;
		loadClasses();
	}

	/**
	 * @param packageName
	 * @param reportUrl
	 * @param port
	 * @param gather
	 */
	public AgentServer(String packageName, String reportUrl, int port, StatusGather gather) {
		this(packageName, reportUrl, port, 5, gather);
	}

	/**
	 * @param packageName
	 * @param reportUrl
	 * @param reportTime
	 * @param gather
	 */
	public AgentServer(String packageName, String reportUrl, long reportTime, StatusGather gather) {
		this(packageName, reportUrl, 42425, reportTime, gather);
	}

	/**
	 * 
	 * @param packageName
	 * @param reportUrl
	 * @param gather
	 */
	public AgentServer(String packageName, String reportUrl, StatusGather gather) {
		this(packageName, reportUrl, 42425, 5, gather);
	}

	/**
	 * @return the gather
	 */
	public StatusGather getGather() {
		return gather;
	}

	/**
	 * @return the packageName
	 */
	public String getPackageName() {
		return packageName;
	}

	/**
	 * @return the port
	 */
	public int getPort() {
		return port;
	}

	/**
	 * @return the reportTime
	 */
	public long getReportTime() {
		return reportTime;
	}

	/**
	 * @return the reportUrl
	 */
	public String getReportUrl() {
		return reportUrl;
	}

	/**
	 * 加载全部服务类,建议使用Ioc容器加载 这样便于管理
	 */
	private void loadClasses() {
		log.debug("loadAllMethod from package " + packageName + " and cache them!");
		serviceClasses = Scans.me().scanPackage(packageName);
		log.debug("load " + serviceClasses.size() + " classes!");
	}

	public void run() {
		TcpServer server = new TcpServer(port, new SocketHandler() {

			@Override
			public void handle(Socket socket) throws IOException {
				BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
				Writer bw = new java.io.OutputStreamWriter(socket.getOutputStream());
				String line;
				while (!Strings.isBlank((line = br.readLine()))) {
					String re = null;
					try {
						re = handleLine(line);
					} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException | InstantiationException e) {
						log.error(e);
						bw.write(Json.toJson(Result.exception(e), JsonFormat.compact()));// 将异常写会客户端
						bw.flush();
					}
					if (!org.nutz.lang.Strings.isBlank(re)) {
						bw.write(re);
						bw.flush();
					}
				}
			}

			private String handleLine(String line) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException, InstantiationException {
				// 反射调用方法并返回
				// NutMap map = Lang.map(line);
				// NutMap method_ = map.getAs("method", NutMap.class);
				InvokeData data = Lang.map2Object(Lang.map(line), InvokeData.class);
				// new InvokeData(new Method(method_.getString("nameSpace"),
				// method_.getString("method")), map.getAs("data",
				// NutMap.class));
				Method method = data.getMethod();
				boolean classFound = false;
				for (Class<?> clazz : serviceClasses) {
					if (Strings.equals(clazz.getAnnotation(NameSpace.class).value(), method.getNameSpace())) {// 找到类
						classFound = true;
						java.lang.reflect.Method[] invokeMethods = clazz.getDeclaredMethods();// 获取该类的全部方法
						for (java.lang.reflect.Method invokeMethod : invokeMethods) {
							if (invokeMethod.getModifiers() == Modifier.PUBLIC && Strings.equals(invokeMethod.getAnnotation(MethodName.class).value(), method.getMethod())) {// 找到方法
								Object obj = invokeMethod.invoke(clazz.newInstance(), data.getData());
								if (obj instanceof Result) {
									return Json.toJson(obj, JsonFormat.compact());
								}
								return Json.toJson(Result.fail("method return an object not instance of Result"), JsonFormat.compact()); // 未返回Result对象
							}
						}
						return Json.toJson(Result.fail("no method named" + method.getMethod() + " found in class " + clazz.getName()), JsonFormat.compact());// 未找到方法
					}
				}
				if (!classFound) {
					return Json.toJson(Result.fail("no class nameSpaceed " + method.getNameSpace() + " found in package " + packageName), JsonFormat.compact());// 未找到类
				}
				return line;
			}

			@Override
			public void whenError(Socket socket, Throwable error) {
				log.error(error);
			}
		});
		server.run();// 让tcpServer跑起来再说

		// 定时汇报任务 5秒一次
		if (gather != null) {
			Tasks.scheduleAtFixedRate(new Runnable() {

				@Override
				public void run() {
					NutMap params = NutMap.NEW();
					params.put("data", Json.toJson(gather.gather()));
					PostSender sender = new PostSender(Request.create(reportUrl, METHOD.POST, params));
					sender.send();
				}
			}, reportTime);
		}

	}

	/**
	 * @param gather
	 *            the gather to set
	 */
	public void setGather(StatusGather gather) {
		this.gather = gather;
	}

	/**
	 * @param packageName
	 *            the packageName to set
	 */
	public void setPackageName(String packageName) {
		this.packageName = packageName;
	}

	/**
	 * @param port
	 *            the port to set
	 */
	public void setPort(int port) {
		this.port = port;
	}

	/**
	 * @param reportTime
	 *            the reportTime to set
	 */
	public void setReportTime(long reportTime) {
		this.reportTime = reportTime;
	}

	/**
	 * @param reportUrl
	 *            the reportUrl to set
	 */
	public void setReportUrl(String reportUrl) {
		this.reportUrl = reportUrl;
	}

	@Override
	public String toString() {
		return "AgentServer [packageName=" + packageName + ", reportUrl=" + reportUrl + ", port=" + port + ", reportTime=" + reportTime + ", gather=" + gather + "]";
	}

}
